﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NUnitResults
{
    /// <summary>
    /// Supplies count information by status for test cases.
    /// </summary>
    public class TestCaseCount : IEnumerable<KeyValuePair<Status, Int32>>
    {
        /// <summary>
        /// A list of counts by status.
        /// </summary>
        private readonly IDictionary<Status, Int32> counts;

        /// <summary>
        /// Gets the number of test cases with the specified status.
        /// </summary>
        /// <param name="status"></param>
        /// <returns></returns>
        public Int32 this[Status status]
        {
            get
            {
                var count = 0;

                if (!counts.TryGetValue(status, out count))
                {
                    return 0;
                }

                return count;
            }
        }

        /// <summary>
        /// Gets the total number of test cases regardless of status.
        /// </summary>
        public Int32 Total
        {
            get
            {
                return counts.Sum(kvp => kvp.Value);
            }
        }

        /// <summary>
        /// Creates a new counts object with the specified counts.
        /// </summary>
        /// <param name="counts"></param>
        public TestCaseCount(IDictionary<Status, Int32> counts)
        {
            if (counts == null)
            {
                throw new ArgumentNullException("counts");
            }

            this.counts = counts;
        }

        #region IEnumerable<KeyValuePair<Status,int>> Members

        /// <summary>
        /// Gets an object which allows iteration over the counts in this object.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<KeyValuePair<Status, int>> GetEnumerator()
        {
            return counts.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Gets an object which allows iteration over the counts in this object.
        /// </summary>
        /// <returns></returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return counts.GetEnumerator();
        }

        #endregion

        #region Statics

        /// <summary>
        /// Produces a TestCaseCount object using the supplied cases, and ensures that all
        /// test cases statusses are included and in a special predefined order.
        /// </summary>
        /// <param name="innerCases"></param>
        /// <returns></returns>
        public static TestCaseCount FromCases(IList<ITestResult> innerCases)
        {
            var testCounts = innerCases.SelectMany(c => c.TestCaseCount)
                                       .GroupBy(c => c.Key)
                                       .Select(g => new KeyValuePair<Status, Int32>(g.Key, g.Sum(kvp => kvp.Value)))
                                       .ToList();

            foreach (Status e in Enum.GetValues(typeof(Status)))
            {
                if (!testCounts.Exists(kvp => kvp.Key == e))
                {
                    testCounts.Add(new KeyValuePair<Status, Int32>(e, 0));
                }
            }

            return new TestCaseCount(testCounts.OrderByDescending(kvp => kvp.Key == Status.Success)
                                               .ThenByDescending(kvp => kvp.Key == Status.Ignored)
                                               .ThenByDescending(kvp => kvp.Key == Status.Inconclusive)
                                               .ThenByDescending(kvp => kvp.Key == Status.NonRunnable)
                                               .ThenByDescending(kvp => kvp.Key == Status.Failed)
                                               .ThenByDescending(kvp => kvp.Key == Status.Error)
                                               .ThenByDescending(kvp => kvp.Key == Status.Unrecognised)
                                               .ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
        }

        #endregion
    }
}
